/* *********************************************************************************  
 *   This file is part of GeometryPlayground.
 *
 *   GeometryPlayground is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   GeometryPlayground is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with GeometryPlayground.  If not, see <http://www.gnu.org/licenses/>.
 **********************************************************************************/
 
import java.awt.Color;
import java.awt.Graphics;
import java.util.LinkedList;

abstract class ToroidalConstruct extends GeoConstruct {
	protected double[] v1={0,0,0},v2={0,0,0};
  protected static double scale=0;//Powers of two
  public static final int scaleLimit=5;
  protected ToroidalConstruct(){}
  public ToroidalConstruct(int t, double[] vector)
  { type=t; setXYZ(vector); setNewXYZ(vector);}
  public ToroidalConstruct(int t, double[] v1, double[] v2)
  { type=t; setXYZ(v1, v2); }  
  public ToroidalConstruct(int t, ToroidalConstruct a, ToroidalConstruct b){
    double[] vec1={0,0,0}, vec2={0,0,0};
    type=t;
    constList.add(a);
    constList.add(b);
    constList.get(0).getXYZ(vec1);
    constList.get(1).getXYZ(vec2);
    this.setXYZ(vec1, vec2);    
  }
  public ToroidalConstruct(int t, double[] v, ToroidalConstruct a) {
    type=t;
    constList.add(a);
    this.setXYZ(v);
  }
  public ToroidalConstruct(int t, LinkedList<GeoConstruct> clickedList){
	  type=t;
	  for(int i=0;i<clickedList.size();i++)
		  constList.addLast(clickedList.get(i));
	  if (constList.size()==2) {
	    double[] vec1={0,0,0}, vec2={0,0,0};
	    constList.get(0).getXYZ(vec1);
        constList.get(1).getXYZ(vec2);
        this.setXYZ(vec1, vec2);
	  }
	  else {
	  }
  }
  public ToroidalConstruct(int t, LinkedList<GeoConstruct> clickedList,double[] v){
	  type=t;
	  for(int i=0;i<clickedList.size();i++)
		  constList.addLast(clickedList.get(i));
	  if (t==GeoConstruct.DISTANCE) {
	    double[] v0={0,0,0};
	    constList.get(0).get(0).getXYZ(v);
		constList.get(0).get(1).getXYZ(v0);
		double dist=2;
		int horiz=0,vert=0;
		for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
	      if (Math.sqrt((v0[0]-v[0]+i)*(v0[0]-v[0]+i)+(v0[1]-v[1]+j)*(v0[1]-v[1]+j))<dist) {
		    dist=Math.sqrt((v0[0]-v[0]+i)*(v0[0]-v[0]+i)+(v0[1]-v[1]+j)*(v0[1]-v[1]+j));
		    horiz=i; vert=j;
	      }
		v[0]=(v[0]+v0[0]+horiz)/2;
		v[1]=(v[1]+v0[1]+vert)/2;
		while (v[0]<0) v[0]+=1;	while (v[0]>1) v[0]-=1;
		while (v[1]<0) v[1]+=1;	while (v[1]>1) v[1]-=1;
	  }
	  if (t==GeoConstruct.RATIO) {
		double[] v0={0,0,0};
		constList.get(0).getXYZ(v);
		constList.get(1).getXYZ(v0);
		double dist=2;
		int horiz=0,vert=0;
		for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
		    if (Math.sqrt((v0[0]-v[0]+i)*(v0[0]-v[0]+i)+(v0[1]-v[1]+j)*(v0[1]-v[1]+j))<dist) {
			  dist=Math.sqrt((v0[0]-v[0]+i)*(v0[0]-v[0]+i)+(v0[1]-v[1]+j)*(v0[1]-v[1]+j));
			  horiz=i; vert=j;
		    }
		v[0]=(v[0]+v0[0]+horiz)/2;
		v[1]=(v[1]+v0[1]+vert)/2;
		while (v[0]<0) v[0]+=1;	while (v[0]>1) v[0]-=1;
		while (v[1]<0) v[1]+=1;	while (v[1]>1) v[1]-=1;
	  }
	  setXYZ(v);
	  update();
  }
  public abstract ToroidalPoint intersect(int m, ToroidalConstruct a);

  public void setXYZ(double[] vector) {
    x=vector[0]; y=vector[1]; z=0;
	newX=x;      newY=y;      newZ=z;
  }

  public void setXYZ(double[] v1, double[] v2) {
    int vert=0,horiz=0;
	double dist=2;
	for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
	if (Math.sqrt((v2[0]-v1[0]+i)*(v2[0]-v1[0]+i)+(v2[1]-v1[1]+j)*(v2[1]-v1[1]+j))<dist) {
		dist=Math.sqrt((v2[0]-v1[0]+i)*(v2[0]-v1[0]+i)+(v2[1]-v1[1]+j)*(v2[1]-v1[1]+j));
		horiz=i; vert=j;
	}
	if(type==LINE || type==SEGMENT){
      x=v2[0]-v1[0]+horiz;y=v2[1]-v1[1]+vert;z=0;
    }
    else{
      x=-v1[1];y=v1[0];z=0;
    }
    setValid(true);
  }

  public void setNewXYZ(double[] vector) {
    newX=vector[0];		newY=vector[1];		newZ=vector[2];
  }

  public void setNewXYZ(double[] v1, double[] v2) {
	double dist=2;
	for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
	if (Math.sqrt((v2[0]-v1[0]+i)*(v2[0]-v1[0]+i)+(v2[1]-v1[1]+j)*(v2[1]-v1[1]+j))<dist) {
		dist=Math.sqrt((v2[0]-v1[0]+i)*(v2[0]-v1[0]+i)+(v2[1]-v1[1]+j)*(v2[1]-v1[1]+j));
	}
	if(type==LINE || type==SEGMENT){
      newX=v2[0]-v1[0];newY=v2[1]-v1[1];newZ=0;
    }
    else {
      newX=-v1[1];newY=v1[0];newZ=0;
    }
  }

  public void translate(double[] dragStart, double[] dragNow){
    double[] vector1={0,0,0};
    getXYZ(vector1);
	for(int i=0;i<3;i++) {
	  vector1[i]+=dragNow[i]-dragStart[i];
	  while(vector1[i]>=1) vector1[i]-=1;
	  while(vector1[i]<0) vector1[i]+=1;
	}
    this.setNewXYZ(vector1);
  }
  public static double getScale(){
    return 1;
  }
  public static void setScale(double s){resetScale();}
  public static void resetScale(){scale=0;}
  
  public static void rescale(double[] v){}

  public static void unscale(double[] v){}
  public void transform(GeoConstruct fixedObject,double[] dragStart,double[] dragNow) {
    MathEqns.transform(fixedObject,this,dragStart,dragNow);
  }
} // end class 


class ToroidalPoint extends ToroidalConstruct{
	protected int sz,cs;
  public ToroidalPoint(int t, double[] vector) {
    super(t,vector);
	for (int i=0;i<2;i++)
	  vector[i]=(double)MathEqns.round(GeoPlayground.getHght()*vector[i])/GeoPlayground.getHght();
	setXYZ(vector);
  }
  public ToroidalPoint(int t, double[] v1, double[] v2) {super(t,v1,v2);} 
  public ToroidalPoint(int t, double[] v, ToroidalConstruct a) {super(t,v,a);}
  public ToroidalPoint(int t, ToroidalConstruct a, ToroidalConstruct b){super(t,a,b);}
  public ToroidalPoint(int t, LinkedList<GeoConstruct> clickedList,double[] v){super(t,clickedList,v);}


  public ToroidalPoint intersect(int m, ToroidalConstruct a){
    ToroidalPoint newPoint=new ToroidalPoint(LINEintLINE0,a,this);
    return newPoint;
  }

  public void draw(Graphics g, int SZ, boolean New) {
    sz=(int)MathEqns.max(4,SZ/40);
    cs=GeoPlayground.getHght();
    if ((New && isRealNew) || (!New && isReal)) { 
	  if(New){
        getNewXYZ(v1);
      }
      else{
        getXYZ(v1);
      }
	  while (v1[0]<0) v1[0]+=1;	while (v1[0]>=1) v1[0]-=1;
	  while (v1[1]<0) v1[1]+=1;	while (v1[1]>=1) v1[1]-=1;
	  if (getType()<30)
	  switch(GeoPlayground.model) {
	    case 0:
		    g.fillOval(MathEqns.round(v1[0]*cs)-sz/2,MathEqns.round(v1[1]*cs)-sz/2,sz,sz);
		    if (getLabelShown())
		      g.drawString(""+(char)('A'+ID%26)+""+(ID/26),
			               MathEqns.round(v1[0]*cs)-sz/2,MathEqns.round(v1[1]*cs)-sz/2);
		  break;
		case 1:
		case 2:
		case 3:
		  v2[0]=SZ+SZ*(2+Math.cos(2*Math.PI*v1[1]))*Math.cos(2*Math.PI*v1[0])/3;
		  v2[1]=SZ+SZ*(2+Math.cos(2*Math.PI*v1[1]))*Math.sin(2*Math.PI*v1[0])/3;
		  if (v1[1]<=.5) {
			  g.fillOval(MathEqns.round(v2[0])-sz/2,MathEqns.round(v2[1])-sz/2,sz,sz);
		      if (getLabelShown())
		        g.drawString(""+(char)('A'+ID%26)+""+(ID/26),
				             MathEqns.round(v2[0])-sz/2,MathEqns.round(v2[1])-sz/2);
		}
	  }
    }
  }

  public boolean mouseIsOver(double[] v1, int SZ){
    double sz=MathEqns.max(6,SZ/20);
	int cs=GeoPlayground.getHght();
	return ((x-v1[0])*(x-v1[0])+(y-v1[1])*(y-v1[1])<(sz*sz/cs/cs));
  }

  public void updatePTonLINE(){
	double[] nm={0,0,0},bn={0,0,0};
	double dist=2;
	int horiz=0,vert=0,min=0;
	setValidNew(get(0).getValidNew());
	if (getValidNew()) {
		constList.get(0).get(0).getNewXYZ(v2);
		constList.get(0).get(1).getNewXYZ(bn);
		if (constList.get(0).getType()==PERP || constList.get(0).getType()==BISECTOR) {
			constList.get(0).getNewXYZ(nm);
			dist=MathEqns.norm(nm);
			v2[0]=bn[0]+constList.get(0).getNewX()/dist*.49;
			v2[1]=bn[1]+constList.get(0).getNewY()/dist*.49;
			v2[2]=0;
			dist=2;
		}
		for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
			if (Math.sqrt((bn[0]-v2[0]+i)*(bn[0]-v2[0]+i)+(bn[1]-v2[1]+j)*(bn[1]-v2[1]+j))<dist) {
				dist=Math.sqrt((bn[0]-v2[0]+i)*(bn[0]-v2[0]+i)+(bn[1]-v2[1]+j)*(bn[1]-v2[1]+j));
				horiz=i; vert=j;
			}
		dist=2;
		for (int i=1;i<49;i++) {
			nm[0]=v2[0]*i/49+(bn[0]+horiz)*(49-i)/49;
			nm[1]=v2[1]*i/49+(bn[1]+vert)*(49-i)/49;
			while (nm[0]<0) nm[0]+=1;	while (nm[0]>=1) nm[0]-=1;
			while (nm[1]<0) nm[1]+=1;	while (nm[1]>=1) nm[1]-=1;
			if (MathEqns.norm(v1,nm)<dist) {
				dist=MathEqns.norm(v1,nm);
				min=i;
			}
		}
		nm[0]=v2[0]*min/49+(bn[0]+horiz)*(49-min)/49;
		while (nm[0]<0) nm[0]+=1;	while (nm[0]>=1) nm[0]-=1;
		nm[1]=v2[1]*min/49+(bn[1]+vert)*(49-min)/49;
		while (nm[1]<0) nm[1]+=1;	while (nm[1]>=1) nm[1]-=1;
		nm[2]=0;
		setNewXYZ(nm);
    }
  }
  public void updatePTonCIRC() {
	double dist=2;
	setValidNew(get(0).getValidNew());
	if (getValidNew()) {
		double[] center={0,0,0},point={0,0,0},u={0,0,0},v={0,0,0},w={0,0,0};
	    constList.get(0).get(0).getNewXYZ(center);
	    constList.get(0).get(1).getNewXYZ(point);
	    w[0]=center[0]+.5;
	    w[1]=center[1]+.5;
	    double r=2;
	    dist=r;
        for (int i=-1;i<2;i++) for (int j=-1;j<2;j++) {
        	v[0]=center[0]+i;
        	v[1]=center[1]+j;
        	if (MathEqns.norm(point,v)<r) r=MathEqns.norm(point,v);
	    }
	    if (r<.5) {
	    	for (int k=0;k<710;k++) {
	    		u[0]=center[0]+r*Math.cos(k/113.);
	    		u[1]=center[1]+r*Math.sin(k/113.);
	    		for (int i=-1;i<2;i++) for (int j=-1;j<2;j++) {
	    			v[0]=v1[0]+i;
	    			v[1]=v1[1]+j;
	    			if (MathEqns.norm(v,u)<dist) {
	    				dist=MathEqns.norm(u,v);
	    				w[0]=u[0];	w[1]=u[1];
	    			}
	    		}
	    	}
	    }
	    else {
	    	double theta=Math.acos(.5/r);
	    	for (double k=theta;k<Math.PI/2-theta;k+=(Math.PI/2-2*theta)/10.) 
	    		for (int l=0;l<4;l++) {
	    			u[0]=center[0]+r*Math.cos(k+l*Math.PI/2);
	    			u[1]=center[1]+r*Math.sin(k+l*Math.PI/2);
	    			for (int i=-1;i<2;i++) for (int j=-1;j<2;j++) {
	    				v[0]=v1[0]+i;
	    				v[1]=v1[1]+j;
	    				if (MathEqns.norm(v,u)<dist) {
	    					dist=MathEqns.norm(u,v);
	    					w[0]=u[0];	w[1]=u[1];
	    				}
	    			}
	    		}
	    }
	    while(w[0]<0) w[0]+=1;	while(w[0]>=1) w[0]-=1;
	    while(w[1]<0) w[1]+=1;	while(w[1]>=1) w[1]-=1;
	    setNewXYZ(w);
	}
		
  }
  public void update() {
    getNewXYZ(v1);
	for (int i=0;i<2;i++) v1[i]=(double)MathEqns.round(GeoPlayground.getHght()*v1[i])/GeoPlayground.getHght();
	while (v1[0]<0) v1[0]+=1;
	while (v1[0]>=1) v1[0]-=1;
	while (v1[1]<0) v1[1]+=1;
	while (v1[1]>=1) v1[1]-=1;
	setNewXYZ(v1);
  }
}

class ToroidalLine extends ToroidalConstruct{
  double[] vec1={0,0,0}, vec2={0,0,0};
  int horiz,vert,cs;
  public ToroidalLine(int t, LinkedList<GeoConstruct> clickedList){
    super(t,clickedList);
  }
  public ToroidalPoint intersect(int m, ToroidalConstruct a){
    ToroidalPoint newPoint;
    if(a.getType()==0)
      newPoint=intersect(m,(ToroidalCircle)a);
    else
      newPoint=intersect(m,(ToroidalLine)a);
    return newPoint;
  }
  public ToroidalPoint intersect(int m, ToroidalLine a){
    ToroidalPoint newPoint=new ToroidalPoint(LINEintLINE0,a,this);
    return newPoint;	// intersections not implemented in Toroidal
  }
  public ToroidalPoint intersect(int m, ToroidalCircle a){
    ToroidalPoint newPoint=a.intersect(m,this);
    return newPoint;	// intersections not implemented in Toroidal
  }
  
  public void draw(Graphics g, int SZ, boolean New) {
    if (isReal) {
      double[] a={0,0,0};
      if(New){
        constList.get(0).getNewXYZ(vec1);
        constList.get(1).getNewXYZ(vec2);
		getNewXYZ(a);
      }
      else{
        constList.get(0).getXYZ(vec1);
        constList.get(1).getXYZ(vec2);
		getXYZ(a);
      }
	  cs=GeoPlayground.getHght();
	  double dist=2;
	  if (type==PERP || type==BISECTOR) {
	    vec1[0]=vec2[0]+a[0];
		vec1[1]=vec2[1]+a[1];
	  }
	  for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
	  if (Math.sqrt((vec2[0]-vec1[0]+i)*(vec2[0]-vec1[0]+i)+(vec2[1]-vec1[1]+j)*(vec2[1]-vec1[1]+j))<dist) {
		dist=Math.sqrt((vec2[0]-vec1[0]+i)*(vec2[0]-vec1[0]+i)+(vec2[1]-vec1[1]+j)*(vec2[1]-vec1[1]+j));
		horiz=i; vert=j;
	  }
	  if (type!=SEGMENT) {
	    double a0=vec2[0]+horiz-vec1[0],
		       a1=vec2[1]+vert-vec1[1],
			   dd=Math.sqrt(a0*a0+a1*a1);
		int b0=MathEqns.round(Math.abs(a0/dd*24)),
		    b1=MathEqns.round(Math.abs(a1/dd*24)), gcd=1;
		for (int i=2;i<=MathEqns.max(b0,b1);i++) if (b0%i==0 && b1%i==0) gcd=i;
		b0/=gcd; b1/=gcd;
		int rd=g.getColor().getRed(),
		    gr=g.getColor().getGreen(),
			bl=g.getColor().getBlue();
		int upTo=0;
		if (b0!=0 && b1!=0) upTo=MathEqns.round(MathEqns.min(Math.abs(48*dd/a0*b0),Math.abs(48*dd/a1*b1)));
		else if (b0!=0) upTo=MathEqns.round(Math.abs(64*dd/a0));
		else if (b1!=0) upTo=MathEqns.round(Math.abs(64*dd/a1));
		for (int j=-(upTo/2);j<=0;j++) for (int jj=0;jj<2;jj++) {
		int i=j; if (jj==1) i=-j;
		  double k=(double)Math.abs(i)*2/upTo;
		  g.setColor(new Color((int)(rd*(1-k)+230*k),
		                       (int)(gr*(1-k)+230*k),
							   (int)(bl*(1-k)+230*k)));	//*/ 
		  v1[0]=vec1[0]*(i)/32/dd+(vec2[0]+horiz)*(32*dd-i)/32/dd;
		  v1[1]=vec1[1]*(i)/32/dd+(vec2[1]+vert)*(32*dd-i)/32/dd;
		  v2[0]=vec1[0]*(1+i)/32/dd+(vec2[0]+horiz)*(32*dd-1-i)/32/dd;
		  v2[1]=vec1[1]*(1+i)/32/dd+(vec2[1]+vert)*(32*dd-1-i)/32/dd;
		  while(v1[1]<0) v1[1]+=1;	while(v1[1]>=1) v1[1]-=1;
		  while(v2[1]<0) v2[1]+=1;	while(v2[1]>=1) v2[1]-=1;
		  switch(GeoPlayground.model) {
		    case 0:
			  while(v1[0]<0) v1[0]+=1;	while(v1[0]>=1) v1[0]-=1;
			  while(v2[0]<0) v2[0]+=1;	while(v2[0]>=1) v2[0]-=1;
			  if (MathEqns.norm(v1,v2)<.5)
			    g.drawLine(MathEqns.round(cs*v1[0]),MathEqns.round(cs*v1[1]),
				           MathEqns.round(cs*v2[0]),MathEqns.round(cs*v2[1]));
			  if (i==3 && getLabelShown())
			    g.drawString(""+(char)('A'+ID%26)+""+(ID/26),MathEqns.round(cs*v1[0]),MathEqns.round(cs*v1[1]));
		      break;
		    case 1:
		    case 2:
		    case 3:
		      int[] w1={0,0,0};
			  w1[0]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*v1[1]))*Math.cos(2*Math.PI*v1[0])/3);
			  w1[1]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*v1[1]))*Math.sin(2*Math.PI*v1[0])/3);
			  if (v2[1]<=.5 && v1[1]<=.5) {
			    int[] w2={0,0,0};
			    w2[0]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*v2[1]))*Math.cos(2*Math.PI*v2[0])/3);
			    w2[1]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*v2[1]))*Math.sin(2*Math.PI*v2[0])/3);
			    g.drawLine(w1[0],w1[1],w2[0],w2[1]);
			    if (i==7 && getLabelShown())
				  g.drawString(""+(char)('A'+ID%26)+""+(ID/26),w1[0],w1[1]);
			  }
			  else g.drawOval(w1[0],w1[1],1,1);
		      break;
		  }
		}
	  }
    }
  }  

  public boolean mouseIsOver(double[] v0, int SZ){
    double[] v2={0,0,0},v1={0,0,0},r={0,0,0};
    constList.get(1).getXYZ(v2);
	double dist=2;
	if (type==PERP || type==BISECTOR) {
	  v1[0]=v2[0]+(x/2);
	  v1[1]=v2[1]+(y/2);
	  v1[2]=0;
	  v2[0]-=(x/2);
	  v2[1]-=(y/2);
	}
	else {//(type==LINE || type==SEGMENT)
	  constList.get(0).getXYZ(v1);
	}
	int horiz=0,vert=0;
	for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
	  if (Math.sqrt((v2[0]-v1[0]+i)*(v2[0]-v1[0]+i)+(v2[1]-v1[1]+j)*(v2[1]-v1[1]+j))<dist) {
	    dist=Math.sqrt((v2[0]-v1[0]+i)*(v2[0]-v1[0]+i)+(v2[1]-v1[1]+j)*(v2[1]-v1[1]+j));
	    horiz=i; vert=j;
	  }
	dist=2;  
	for (int i=0;i<=100;i++) {
	  r[0]=v1[0]*i/100+(v2[0]+horiz)*(100-i)/100;
	  while(r[0]<0) r[0]+=1;	while(r[0]>=1)r[0]-=1;
	  r[1]=v1[1]*i/100+(v2[1]+vert)*(100-i)/100;
	  while(r[1]<0) r[1]+=1;	while(r[1]>=1)r[1]-=1;
	  if (MathEqns.norm(r,v0)<dist) dist=MathEqns.norm(r,v0);
	}
    return (dist<.0125);
  }
  public void update() {
    //boolean nowValid=true;
	//for (int i=0;i<getSize();i++) nowValid=(nowValid && constList.get(i).getValidNew());
	//setValidNew(nowValid);
	//if (nowValid) {
        constList.get(0).getNewXYZ(v1);
        constList.get(1).getNewXYZ(v2);
		int vert=0,horiz=0;
		double dist=2;
		for (int i=-1;i<2;i++) for (int j=-1;j<2;j++) 
		  if (Math.sqrt((v2[0]-v1[0]+i)*(v2[0]-v1[0]+i)+(v2[1]-v1[1]+j)*(v2[1]-v1[1]+j))<dist) {
		    dist=Math.sqrt((v2[0]-v1[0]+i)*(v2[0]-v1[0]+i)+(v2[1]-v1[1]+j)*(v2[1]-v1[1]+j));
			horiz=i; vert=j;
		  }
		v2[0]+=horiz;
		v2[1]+=vert;
        if (type==LINE || type==SEGMENT) {
        	double[] norm={0,0,0};
		    norm[0]=v2[0]-v1[0];
		    norm[1]=v2[1]-v1[1];
		    norm[2]=0;
		    if (MathEqns.norm(norm)<.00001) setValidNew(false);
            else setNewXYZ(norm);
        }
      //}
  }
}

class ToroidalCircle extends ToroidalConstruct{

  public ToroidalCircle(int t, ToroidalConstruct a, ToroidalConstruct b) {
    super(t,a,b);
    if (a.getValid() && b.getValid()) setValid(true);
  }
  public ToroidalPoint intersect(int m, ToroidalConstruct a){
    ToroidalPoint newPoint;
    if(a.getType()==0)
      newPoint=intersect(m,(ToroidalCircle)a);
    else
      newPoint=intersect(m,(ToroidalLine)a);
    return newPoint;
  }
  public ToroidalPoint intersect(int m, ToroidalLine a){
    ToroidalPoint newPoint=new ToroidalPoint(CIRCintLINE0+m,this,a);
    return newPoint;	// intersections not implemented in Toroidal
  }

  public ToroidalPoint intersect(int m, ToroidalCircle a){
    ToroidalPoint newPoint=new ToroidalPoint(CIRCintCIRC00+m,this,a);
    return newPoint;	// intersections not implemented in Toroidal
  }

  public void draw(Graphics g, int SZ, boolean New) {
    if ((New && isRealNew) || (!New && isReal)) {
      double[] center={0,0,0},radial={0,0,0},u={0,0,0},v={0,0,0};
      int cs=GeoPlayground.getHght();
	  if(New){
        constList.get(0).getNewXYZ(center);
        constList.get(1).getNewXYZ(radial);
      }
      else{
        constList.get(0).getXYZ(center);
        constList.get(1).getXYZ(radial);
      }
      double r=2;
	  for (int i=-1;i<2;i++) for (int j=-1;j<2;j++) {
	    u[0]=radial[0];
		u[1]=radial[1];
		v[0]=center[0]+i;
		v[1]=center[1]+j;
	    if (MathEqns.norm(u,v)<r) {
		  r=MathEqns.norm(u,v);
		}
	  }
	  if (getShown()) switch(GeoPlayground.model) {
	    case 0:
		  if (r<=.5) {
			for (int i=0;i<176;i++) {
			  int x1=MathEqns.round(cs*(center[0]+r*Math.cos(i/28.))),
			      y1=MathEqns.round(cs*(center[1]+r*Math.sin(i/28.))),
				  x2=MathEqns.round(cs*(center[0]+r*Math.cos((i+1)/28.))),
				  y2=MathEqns.round(cs*(center[1]+r*Math.sin((i+1)/28.)));
			  while (x1<0) x1+=cs; while (x1>=cs) x1-=cs;
			  while (x2<0) x2+=cs; while (x2>=cs) x2-=cs;
			  while (y1<0) y1+=cs; while (y1>=cs) y1-=cs;
			  while (y2<0) y2+=cs; while (y2>=cs) y2-=cs;
			  if (Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))<SZ)
			    g.drawLine(x1,y1,x2,y2);
			}
			if (getLabelShown())
			  g.drawString(""+(char)('A'+ID%26)+""+(ID/26),
			               MathEqns.round(cs*(center[0]+r*.707))%cs,
			               MathEqns.round(cs*(center[1]+r*.707))%cs);
			}
		  else {
			int theta=(int)(180*Math.acos(.5/r)/Math.PI);
			for (int i=theta;i<90-theta;i++) for (int j=0;j<4;j++) {
			  int x1=(int)(cs*(center[0]+r*Math.cos(i*Math.PI/180+j*Math.PI/2))),
			      y1=(int)(cs*(center[1]+r*Math.sin(i*Math.PI/180+j*Math.PI/2))),
				  x2=(int)(cs*(center[0]+r*Math.cos((i+1)*Math.PI/180+j*Math.PI/2))),
			      y2=(int)(cs*(center[1]+r*Math.sin((i+1)*Math.PI/180+j*Math.PI/2)));
			  while (x1<0) x1+=cs; while (x1>cs) x1-=cs;
			  while (x2<0) x2+=cs; while (x2>cs) x2-=cs;
			  while (y1<0) y1+=cs; while (y1>cs) y1-=cs;
			  while (y2<0) y2+=cs; while (y2>cs) y2-=cs;
			  if (Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))<SZ) g.drawLine(x1,y1,x2,y2);
			}
			if (getLabelShown()) {
			  g.drawString(""+(char)('A'+ID%26)+""+(ID/26),
			               MathEqns.round(cs*(center[0]+r*.707))%cs,
			               MathEqns.round(cs*(center[1]+r*.707))%cs);
			}
		  }
		  break;
		case 1:
		case 2:
		case 3:
		  if (r<=.5)
		  for (int i=0;i<88;i++) {
		    u[0]=center[0]+r*Math.cos(i/14.);
			while(u[0]<0) u[0]+=1;	while(u[0]>=1) u[0]-=1;
			u[1]=center[1]+r*Math.sin(i/14.);
			while(u[1]<0) u[1]+=1;	while(u[1]>=1) u[1]-=1;
			v[0]=center[0]+r*Math.cos((i+1)/14.);
			while(v[0]<0) v[0]+=1;	while(v[0]>=1) v[0]-=1;
			v[1]=center[1]+r*Math.sin((i+1)/14.);
			while(v[1]<0) v[1]+=1;	while(v[1]>=1) v[1]-=1;
			int[] w1={0,0,0};
			w1[0]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*u[1]))*Math.cos(2*Math.PI*u[0])/3);
			w1[1]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*u[1]))*Math.sin(2*Math.PI*u[0])/3);
			if (v[1]<=.5 && u[1]<=.5) {
			  int[] w2={0,0,0};
			  w2[0]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*v[1]))*Math.cos(2*Math.PI*v[0])/3);
			  w2[1]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*v[1]))*Math.sin(2*Math.PI*v[0])/3);
			  g.drawLine(w1[0],w1[1],w2[0],w2[1]);
			  if (i==11 && getLabelShown())
				g.drawString(""+(char)('A'+ID%26)+""+(ID/26),w1[0],w1[1]);
			}
			else g.drawOval(w1[0],w1[1],1,1);
		  }
		  else {
		    double theta=Math.acos(.5/r);
		    for (int l=0;l<4;l++) 
			for (int i=0;i<32;i++) {
			  u[0]=center[0]+r*Math.cos(l*Math.PI/2+theta+i*(Math.PI/2-2*theta)/32);
			  while(u[0]<0) u[0]+=1;	while(u[0]>=1) u[0]-=1;
			  u[1]=center[1]+r*Math.sin(l*Math.PI/2+theta+i*(Math.PI/2-2*theta)/32);
			  while(u[1]<0) u[1]+=1;	while(u[1]>=1) u[1]-=1;
			  v[0]=center[0]+r*Math.cos(l*Math.PI/2+theta+(i+1)*(Math.PI/2-2*theta)/32);
			  while(v[0]<0) v[0]+=1;	while(v[0]>=1) v[0]-=1;
			  v[1]=center[1]+r*Math.sin(l*Math.PI/2+theta+(i+1)*(Math.PI/2-2*theta)/32);
			  while(v[1]<0) v[1]+=1;	while(v[1]>=1) v[1]-=1;
			  int[] w1={0,0,0};
			  w1[0]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*u[1]))*Math.cos(2*Math.PI*u[0])/3);
			  w1[1]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*u[1]))*Math.sin(2*Math.PI*u[0])/3);
			  if (v[1]<=.5 && u[1]<=.5) {
			    int[] w2={0,0,0};
			    w2[0]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*v[1]))*Math.cos(2*Math.PI*v[0])/3);
			    w2[1]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*v[1]))*Math.sin(2*Math.PI*v[0])/3);
			    g.drawLine(w1[0],w1[1],w2[0],w2[1]);
			    if (l==0 && i==theta && getLabelShown())
				  g.drawString(""+(char)('A'+ID%26)+""+(ID/26),w1[0],w1[1]);
			  }
			  else g.drawOval(w1[0],w1[1],1,1);
			}
		  }
		  break;	  
	  }
    }
  }
  public boolean mouseIsOver(double[] mouse, int SZ){
    double[] center={0,0,0}, point={0,0,0},u={0,0,0},v={0,0,0};
    int cs=GeoPlayground.getHght();
	double r=2,radius=2;
    constList.get(0).getXYZ(center);
    constList.get(1).getXYZ(point);
	for (int i=-1;i<2;i++) for (int j=-1;j<2;j++) {
	  u[0]=point[0];
	  u[1]=point[1];
	  v[0]=center[0]+i;
	  v[1]=center[1]+j;
	  if (MathEqns.norm(u,v)<r) r=MathEqns.norm(u,v);
	  if (MathEqns.norm(v,mouse)<radius) radius=MathEqns.norm(v,mouse);
	}
	return Math.abs(cs*(radius-r))<3;
  }

  public void update() {
    setValidNew(constList.get(0).getValidNew() && constList.get(1).getValidNew());
  }

  public double[] getCLxyz(ToroidalPoint inter, ToroidalConstruct b, int i) {
    double[] u={0,0,0},v={0,0,0},w={0,0,0},z={0,0,0},x={0,0,0};
    constList.get(0).getXYZ(u);          // a is a circle w/ ctr A 
    constList.get(1).getXYZ(v);          //& pt B
    b.getXYZ(w);          // b is a line with slope w
    b.constList.get(1).getXYZ(z);        // and point z, we find a pt of intersection.
        
    inter.setValid(CircleEqns.calculateEucCL(u,v,w,z,x,(i==0)));
    return x;
  }


  public double[] getNewCLxyz(ToroidalPoint inter,ToroidalConstruct b, int i) {
    double[] u={0,0,0},v={0,0,0},w={0,0,0},z={0,0,0},x={0,0,0};
    constList.get(0).getNewXYZ(u);          // a is a circle w/ ctr A & pt B
    constList.get(1).getNewXYZ(v);          // b is a line
    b.getNewXYZ(w);          // we find a pt of intersection.
    b.constList.get(1).getNewXYZ(z);
    
    inter.setValid(CircleEqns.calculateEucCL(u,v,w,z,x,(i==0)));
    return x;
  }

  public double[] getCCxyz(ToroidalPoint inter,ToroidalConstruct b, int i) {
    double[] t={0,0,0},u={0,0,0},v={0,0,0},w={0,0,0},x={0,0,0};
    constList.get(0).getXYZ(t);          // a is a circle
    constList.get(1).getXYZ(u);          // b is a circle
    b.get(0).getXYZ(v);       // we find a pt of intersection.
    b.get(1).getXYZ(w);
    inter.setValid(CircleEqns.calculateEucCC(t,u,v,w,x,(i==0)));
    return x;
  }
  public double[] getNewCCxyz(ToroidalPoint inter,ToroidalConstruct b, int i) {
    double[] t={0,0,0},u={0,0,0},v={0,0,0},w={0,0,0},x={0,0,0};
    constList.get(0).getNewXYZ(t);         // a is a circle
    constList.get(1).getNewXYZ(u);         // b is a circle
    b.get(0).getNewXYZ(v);      // we find a pt of intersection.
    b.get(1).getNewXYZ(w);
    inter.isRealNew=CircleEqns.calculateEucCC(t,u,v,w,x,(i==0));
    return x;
  }  
}

class ToroidalPTonLINE extends ToroidalPoint {
	public ToroidalPTonLINE(int t, double[] vector) {super(t, vector);}
	public ToroidalPTonLINE(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalPTonLINE(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalPTonLINE(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalPTonLINE(int t, LinkedList<GeoConstruct> clickedList,double[] v) {super(t, clickedList, v);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
	}
	public void update() {
		super.update();
		updatePTonLINE();
	}
}

class ToroidalPTonCIRC extends ToroidalPoint {
	public ToroidalPTonCIRC(int t, double[] vector) {super(t, vector);}
	public ToroidalPTonCIRC(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalPTonCIRC(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalPTonCIRC(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalPTonCIRC(int t, LinkedList<GeoConstruct> clickedList,double[] v) {super(t, clickedList, v);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
	}
	public void update() {
		super.update();
		updatePTonCIRC();
	}
}

/*class ToroidalLINEintLINE extends ToroidalPoint {
	public ToroidalLINEintLINE(int t, double[] vector) {super(t, vector);}
	public ToroidalLINEintLINE(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalLINEintLINE(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalLINEintLINE(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalLINEintLINE(int t, LinkedList<GeoConstruct> clickedList,double[] v) {super(t, clickedList, v);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
	}
	public void update() {
		super.update();
	}
}


class ToroidalCIRCintLINE extends ToroidalPoint {
	public ToroidalCIRCintLINE(int t, double[] vector) {super(t, vector);}
	public ToroidalCIRCintLINE(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalCIRCintLINE(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalCIRCintLINE(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalCIRCintLINE(int t, LinkedList<GeoConstruct> clickedList,double[] v) {super(t, clickedList, v);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
	}
	public void update() {
		super.update();
	}
}

class ToroidalCIRCintCIRC extends ToroidalPoint {
	public ToroidalCIRCintCIRC(int t, double[] vector) {super(t, vector);}
	public ToroidalCIRCintCIRC(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalCIRCintCIRC(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalCIRCintCIRC(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalCIRCintCIRC(int t, LinkedList<GeoConstruct> clickedList,double[] v) {super(t, clickedList, v);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
	}
	public void update() {
		super.update();
	}
}*/

class ToroidalMIDPT extends ToroidalPoint {
	public ToroidalMIDPT(int t, double[] vector) {super(t, vector);}
	public ToroidalMIDPT(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalMIDPT(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalMIDPT(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalMIDPT(int t, LinkedList<GeoConstruct> clickedList,double[] v) {super(t, clickedList, v);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
	}
	public void update() {
		super.update();
		double[] nm={0,0,0};
		double dist=2;
		int horiz=0,vert=0;
		constList.get(0).getNewXYZ(v1);
		constList.get(1).getNewXYZ(v2);
		for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
	    if (Math.sqrt((v1[0]-v2[0]+i)*(v1[0]-v2[0]+i)+(v1[1]-v2[1]+j)*(v1[1]-v2[1]+j))<dist) {
		  dist=Math.sqrt((v1[0]-v2[0]+i)*(v1[0]-v2[0]+i)+(v1[1]-v2[1]+j)*(v1[1]-v2[1]+j));
		  horiz=i; vert=j;
	    }
		nm[0]=(v1[0]+horiz+v2[0])/2;
		nm[1]=(v1[1]+vert+v2[1])/2;
		nm[2]=0;
		while (nm[0]<0) nm[0]+=1;	while (nm[0]>1) nm[0]-=1;
		while (nm[1]<0) nm[1]+=1;	while (nm[1]>1) nm[1]-=1;
		setNewXYZ(nm);
	}
}

class ToroidalFIXedPT extends ToroidalPoint {
	public ToroidalFIXedPT(int t, double[] vector) {super(t, vector);}
	public ToroidalFIXedPT(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalFIXedPT(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalFIXedPT(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalFIXedPT(int t, LinkedList<GeoConstruct> clickedList,double[] v) {super(t, clickedList, v);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
	}
	public void update() {
		super.update();
	}
}
class ToroidalMeasure extends ToroidalPoint {
    protected double[] a={0,0,0},b={0,0,0};
    double dist=2;
	public ToroidalMeasure(int t, double[] vector) {super(t, vector);}
	public ToroidalMeasure(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalMeasure(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalMeasure(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalMeasure(int t, LinkedList<GeoConstruct> clickedList,double[] v) {super(t, clickedList, v);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
		if (New && (getType()!=ANGLE && getType()!=RATIO)) {
			get(0).get(0).getNewXYZ(a);
			get(0).get(1).getNewXYZ(b);
		}
		else if (getType()!=ANGLE && getType()!=RATIO) {
			get(0).get(0).getXYZ(a);
			get(0).get(1).getXYZ(b);
		}
		dist=2;
		for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
			if (Math.sqrt((a[0]-b[0]+i)*(a[0]-b[0]+i)+(a[1]-b[1]+j)*(a[1]-b[1]+j))<dist)
				dist = Math.sqrt((a[0]-b[0]+i)*(a[0]-b[0]+i)+(a[1]-b[1]+j)*(a[1]-b[1]+j));
		switch(GeoPlayground.model) {
	    case 0:
			g.fillRect(MathEqns.round(v1[0]*cs)-sz/2+1,MathEqns.round(v1[1]*cs)-sz/2+1,sz-2,sz-2);
			g.drawLine(MathEqns.round(v1[0]*cs),MathEqns.round(v1[1]*cs),MathEqns.round(v1[0]*cs)+10,MathEqns.round(v1[1]*cs)-5);
		  break;
		case 1:
		case 2:
		case 3:
			v2[0]=SZ+SZ*(2+Math.cos(2*Math.PI*v1[1]))*Math.cos(2*Math.PI*v1[0])/3;
			v2[1]=SZ+SZ*(2+Math.cos(2*Math.PI*v1[1]))*Math.sin(2*Math.PI*v1[0])/3;
		  if (v1[1]<=.5) {
			for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
			  if (Math.sqrt((a[0]-b[0]+i)*(a[0]-b[0]+i)+(a[1]-b[1]+j)*(a[1]-b[1]+j))<dist)
				dist = Math.sqrt((a[0]-b[0]+i)*(a[0]-b[0]+i)+(a[1]-b[1]+j)*(a[1]-b[1]+j));
			g.fillOval(MathEqns.round(v2[0])-sz/2+1,MathEqns.round(v2[1])-sz/2+1,sz-2,sz-2);
			g.drawLine(MathEqns.round(v2[0]),MathEqns.round(v2[1]),MathEqns.round(v2[0])+10,MathEqns.round(v2[1])-5);
		}
	  }
	}
}
class ToroidalRATIO extends ToroidalMeasure {
	public ToroidalRATIO(int t, double[] vector) {super(t, vector);}
	public ToroidalRATIO(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalRATIO(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalRATIO(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalRATIO(int t, LinkedList<GeoConstruct> clickedList,double[] v) {
		super(t, clickedList, v);
	}
	public void draw(Graphics g, int SZ, boolean New) {
		if ((New && getValidNew()) || (!New && getValid())) {
		super.draw(g, SZ, New);
		String[] ratioText={"",""};
		double[] ratioVar={0,0};
		for (int i=0;i<2;i++) {
			if (New) {
				get(i).get(0).get(0).getNewXYZ(a);
				get(i).get(0).get(1).getNewXYZ(b);
			}
			else {
				get(i).get(0).get(0).getXYZ(a);
				get(i).get(0).get(1).getXYZ(b);
			}
			switch(get(i).getType()) {
			case DISTANCE:
				dist=2;
				for (int k=-1;k<2;k++) for (int j=-1;j<2;j++)
					if (Math.sqrt((a[0]-b[0]+k)*(a[0]-b[0]+k)+(a[1]-b[1]+j)*(a[1]-b[1]+j))<dist)
						dist = Math.sqrt((a[0]-b[0]+k)*(a[0]-b[0]+k)+(a[1]-b[1]+j)*(a[1]-b[1]+j));
				ratioText[i]="d("+(char)('A'+get(i).get(0).get(0).getID()%26)+""+(get(i).get(0).get(0).getID()/26)+","
				+(char)('A'+get(i).get(0).get(1).getID()%26)+""+(get(i).get(0).get(1).getID()/26)+")";
				ratioVar[i]=dist;
				break;
			case ANGLE:
				double[] c={0,0,0};
				if (New) get(i).get(0).get(2).getNewXYZ(c);
				else get(i).get(0).get(2).getXYZ(c);
				int horiz=0,vert=0;
				dist=2;
				for (int k=-1;k<2;k++) for (int j=-1;j<2;j++)
				if (Math.sqrt((a[0]-b[0]+k)*(a[0]-b[0]+k)+(a[1]-b[1]+j)*(a[1]-b[1]+j))<dist) {
				  dist = Math.sqrt((a[0]-b[0]+k)*(a[0]-b[0]+k)+(a[1]-b[1]+j)*(a[1]-b[1]+j));
				  horiz=k; vert=j;
				}
				a[0]+=horiz;
				a[1]+=vert;
				dist=2;
				for (int k=-1;k<2;k++) for (int j=-1;j<2;j++)
				if (Math.sqrt((c[0]-b[0]+k)*(c[0]-b[0]+k)+(c[1]-b[1]+j)*(c[1]-b[1]+j))<dist) {
				  dist = Math.sqrt((c[0]-b[0]+k)*(c[0]-b[0]+k)+(c[1]-b[1]+j)*(c[1]-b[1]+j));
				  horiz=k; vert=j;
				}
				c[0]+=horiz;
				c[1]+=vert;			
				ratioText[i]="m\u2220("+(char)('A'+get(i).get(0).get(0).getID()%26)+""+(get(i).get(0).get(0).getID()/26)+","
				+(char)('A'+get(i).get(0).get(1).getID()%26)+""+(get(i).get(0).get(1).getID()/26)+","
				+(char)('A'+get(i).get(0).get(2).getID()%26)+""+(get(i).get(0).get(2).getID()/26)+")";
				ratioVar[i]=MathEqns.eucAngle(a,b,c);
				break;
			case AREA:
				dist=2;
				for (int k=-1;k<2;k++) for (int j=-1;j<2;j++)
					if (Math.sqrt((a[0]-b[0]+k)*(a[0]-b[0]+k)+(a[1]-b[1]+j)*(a[1]-b[1]+j))<dist)
						dist = Math.sqrt((a[0]-b[0]+k)*(a[0]-b[0]+k)+(a[1]-b[1]+j)*(a[1]-b[1]+j));
				double area=dist*dist;
				if (dist>.5) area=area*(1-4/Math.PI*Math.acos(1/(2*dist)))+Math.sqrt(4*dist*dist-1)/Math.PI;
				ratioText[i]="a("+(char)('A'+get(i).get(0).getID()%26)+""+(get(i).get(0).getID()/26)+")";
				ratioVar[i]=area*Math.PI;
				break;
			case CIRCUMF:
				dist=2;
				for (int k=-1;k<2;k++) for (int j=-1;j<2;j++)
					if (Math.sqrt((a[0]-b[0]+k)*(a[0]-b[0]+k)+(a[1]-b[1]+j)*(a[1]-b[1]+j))<dist)
						dist = Math.sqrt((a[0]-b[0]+k)*(a[0]-b[0]+k)+(a[1]-b[1]+j)*(a[1]-b[1]+j));
				double circ=2*dist;
				if (dist>.5) circ*=(1-4/Math.PI*Math.acos(1/(2*dist)));
				ratioText[i]="c("+(char)('A'+get(i).get(0).getID()%26)+""+(get(i).get(0).getID()/26)+")";
				ratioVar[i]=circ*Math.PI;
				break;
			}
		}
		String calcText="-";
		if (ratioVar[1]!=0) calcText=""+MathEqns.chop(ratioVar[0]/ratioVar[1]);
		switch (GeoPlayground.model) {
		case 0:
			g.drawString(ratioText[0]+":"+ratioText[1]+"\u2248"+calcText,
					     MathEqns.round(v1[0]*cs)+13,MathEqns.round(v1[1]*cs));
			break;
		case 1:
		case 2:
		case 3:
			if (v1[1]<=.5)
				g.drawString(ratioText[0]+":"+ratioText[1]+"\u2248"+calcText,
						     MathEqns.round(v2[0])+13,MathEqns.round(v2[1]));
			break;
		}
	  }
	}
	public void update() {
		super.update();
	}
}


class ToroidalDISTANCE extends ToroidalMeasure {
	public ToroidalDISTANCE(int t, double[] vector) {super(t, vector);}
	public ToroidalDISTANCE(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalDISTANCE(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalDISTANCE(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalDISTANCE(int t, LinkedList<GeoConstruct> clickedList,double[] v) {super(t, clickedList, v);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
		switch (GeoPlayground.model) {
			case 0:
				g.drawString("d("+(char)('A'+get(0).get(0).getID()%26)+""+(get(0).get(0).getID()/26)+","
					+(char)('A'+get(0).get(1).getID()%26)+""+(get(0).get(1).getID()/26)+")\u2248"
					+MathEqns.chop(dist),MathEqns.round(v1[0]*cs)+13,MathEqns.round(v1[1]*cs));
				break;
			case 1:
			case 2:
			case 3:
				if (v1[1]<=.5)
					g.drawString("d("+(char)('A'+get(0).get(0).getID()%26)+""+(get(0).get(0).getID()/26)+","
							+(char)('A'+get(0).get(1).getID()%26)+""+(get(0).get(1).getID()/26)+")\u2248"
							+MathEqns.chop(dist),MathEqns.round(v2[0])+13,MathEqns.round(v2[1]));
				break;
		}
	}
	public void update() {
		super.update();
		updatePTonLINE();
	}
}

class ToroidalANGLE extends ToroidalMeasure {
	public ToroidalANGLE(int t, double[] vector) {super(t, vector);}
	public ToroidalANGLE(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalANGLE(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalANGLE(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalANGLE(int t, LinkedList<GeoConstruct> clickedList,double[] v) {super(t, clickedList, v);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
		double[] c={0,0,0};
		switch (GeoPlayground.model) {
			case 0:
				if (New) get(0).get(2).getNewXYZ(c);
				else get(0).get(2).getXYZ(c);
				int horiz=0,vert=0;
				dist=2;
				for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
				if (Math.sqrt((a[0]-b[0]+i)*(a[0]-b[0]+i)+(a[1]-b[1]+j)*(a[1]-b[1]+j))<dist) {
				  dist = Math.sqrt((a[0]-b[0]+i)*(a[0]-b[0]+i)+(a[1]-b[1]+j)*(a[1]-b[1]+j));
				  horiz=i; vert=j;
				}
				a[0]+=horiz;
				a[1]+=vert;
				dist=2;
				for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
				if (Math.sqrt((c[0]-b[0]+i)*(c[0]-b[0]+i)+(c[1]-b[1]+j)*(c[1]-b[1]+j))<dist) {
				  dist = Math.sqrt((c[0]-b[0]+i)*(c[0]-b[0]+i)+(c[1]-b[1]+j)*(c[1]-b[1]+j));
				  horiz=i; vert=j;
				}
				c[0]+=horiz;
				c[1]+=vert;			
				g.drawString("m\u2220("+(char)('A'+get(0).get(0).getID()%26)+""+(get(0).get(0).getID()/26)+","
						   +(char)('A'+get(0).get(1).getID()%26)+""+(get(0).get(1).getID()/26)+","
						   +(char)('A'+get(0).get(2).getID()%26)+""+(get(0).get(2).getID()/26)+")\u2248"
						   +MathEqns.chop(MathEqns.eucAngle(a,b,c))+"\u00b0",
						   (int)(cs*v1[0])+13,(int)(cs*v1[1]));
				break;
			case 1:
			case 2:
			case 3:
				if (v1[1]<=.5) {
					if (New) get(0).get(2).getNewXYZ(c);
					else get(0).get(2).getXYZ(c);
					horiz=0;vert=0;
					dist=2;
					for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
						if (Math.sqrt((a[0]-b[0]+i)*(a[0]-b[0]+i)+(a[1]-b[1]+j)*(a[1]-b[1]+j))<dist) {
							dist = Math.sqrt((a[0]-b[0]+i)*(a[0]-b[0]+i)+(a[1]-b[1]+j)*(a[1]-b[1]+j));
							horiz=i; vert=j;
						}
					a[0]+=horiz;
					a[1]+=vert;
					dist=2;
					for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
						if (Math.sqrt((c[0]-b[0]+i)*(c[0]-b[0]+i)+(c[1]-b[1]+j)*(c[1]-b[1]+j))<dist) {
							dist = Math.sqrt((c[0]-b[0]+i)*(c[0]-b[0]+i)+(c[1]-b[1]+j)*(c[1]-b[1]+j));
							horiz=i; vert=j;
						}
					c[0]+=horiz;
					c[1]+=vert;			
					g.drawString("m\u2220("+(char)('A'+get(0).get(0).getID()%26)+""+(get(0).get(0).getID()/26)+","
						   +(char)('A'+get(0).get(1).getID()%26)+""+(get(0).get(1).getID()/26)+","
						   +(char)('A'+get(0).get(2).getID()%26)+""+(get(0).get(2).getID()/26)+")\u2248"
						   +MathEqns.chop(MathEqns.eucAngle(a,b,c),1)+"\u00b0",
						   MathEqns.round(v2[0])+13,MathEqns.round(v2[1]));
				}
				break;
		}
	}
	public void update() {
		super.update();
		updatePTonLINE();
	}
}

class ToroidalCIRCUMF extends ToroidalMeasure {
	public ToroidalCIRCUMF(int t, double[] vector) {super(t, vector);}
	public ToroidalCIRCUMF(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalCIRCUMF(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalCIRCUMF(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalCIRCUMF(int t, LinkedList<GeoConstruct> clickedList,double[] v) {super(t, clickedList, v);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
		double circ=2*dist;
		switch (GeoPlayground.model){
			case 0:
				if (dist>.5) circ*=(1-4/Math.PI*Math.acos(1/(2*dist)));
				g.drawString("c("+(char)('A'+get(0).getID()%26)+""+(get(0).getID()/26)+")\u2248"
					   +MathEqns.chop(circ)+"\u03c0",MathEqns.round(v1[0]*cs)+13,MathEqns.round(v1[1]*cs));
				break;
			case 1:
			case 2:
			case 3:
				if (dist>.5) circ*=(1-4/Math.PI*Math.acos(1/(2*dist)));
				if (v1[1]<=.5)
					g.drawString("c("+(char)('A'+get(0).getID()%26)+""+(get(0).getID()/26)+")\u2248"
						   +MathEqns.chop(circ)+"\u03c0",MathEqns.round(v2[0])+13,MathEqns.round(v2[1]));
				
				break;
		}
	}
	public void update() {
		super.update();
		updatePTonCIRC();
	}
}

class ToroidalAREA extends ToroidalMeasure {
	public ToroidalAREA(int t, double[] vector) {super(t, vector);}
	public ToroidalAREA(int t, double[] v1, double[] v2) {super(t, v1, v2);}
	public ToroidalAREA(int t, double[] v, ToroidalConstruct a) {super(t, v, a);}
	public ToroidalAREA(int t, ToroidalConstruct a,ToroidalConstruct b) {super(t, a, b);}
	public ToroidalAREA(int t, LinkedList<GeoConstruct> clickedList,double[] v) {super(t, clickedList, v);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
		double area=dist*dist;
		if (dist>.5) area=area*(1-4/Math.PI*Math.acos(1/(2*dist)))+Math.sqrt(4*dist*dist-1)/Math.PI;
		switch (GeoPlayground.model){
			case 0:
				g.drawString("a("+(char)('A'+get(0).getID()%26)+""+(get(0).getID()/26)+")\u2248"
					   +MathEqns.chop(area)+"\u03c0",MathEqns.round(v1[0]*cs)+13,MathEqns.round(v1[1]*cs));
				break;
			case 1:
			case 2:
			case 3:
				if (v1[1]<=.5)
					g.drawString("a("+(char)('A'+get(0).getID()%26)+""+(get(0).getID()/26)+")\u2248"
					   +MathEqns.chop(area)+"\u03c0",MathEqns.round(v2[0])+13,MathEqns.round(v2[1]));
				break;
		}
	}
	public void update() {
		super.update();
		updatePTonCIRC();
	}
}

class ToroidalPERP extends ToroidalLine {
	public ToroidalPERP(int t, LinkedList<GeoConstruct> clickedList) {super(t, clickedList);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
	}
	public void update() {
		super.update();
		if(getValidNew()) {
			v2[0]=-v1[1];
			v2[1]=v1[0];
			setNewXYZ(v2);
		}  
	}
}

class ToroidalSEGMENT extends ToroidalLine {
	public ToroidalSEGMENT(int t, LinkedList<GeoConstruct> clickedList) {super(t, clickedList);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
		for (int i=0;i<20;i++) {
			  v1[0]=vec1[0]*i/20+(vec2[0]+horiz)*(20-i)/20;
			  v1[1]=vec1[1]*i/20+(vec2[1]+vert)*(20-i)/20;
			  v2[0]=vec1[0]*(i+1)/20+(vec2[0]+horiz)*(19-i)/20;
			  v2[1]=vec1[1]*(i+1)/20+(vec2[1]+vert)*(19-i)/20;
			  switch(GeoPlayground.model) {
		        case 0:
			      while(v1[0]<0) v1[0]+=1;	while(v1[0]>=1) v1[0]-=1;
				  while(v1[1]<0) v1[1]+=1;	while(v1[1]>=1) v1[1]-=1;
				  while(v2[0]<0) v2[0]+=1;	while(v2[0]>=1) v2[0]-=1;
				  while(v2[1]<0) v2[1]+=1;	while(v2[1]>=1) v2[1]-=1;
				  if (MathEqns.norm(v1,v2)<.5)
				    g.drawLine((int)(v1[0]*cs),(int)(v1[1]*cs),(int)(v2[0]*cs),(int)(v2[1]*cs));
				  if (i==7 && getLabelShown())
				    g.drawString(""+(char)('A'+ID%26)+""+(ID/26),(int)(v1[0]*cs),(int)(v1[1]*cs));
				  break;
			    case 1:
			    case 2:
			    case 3:
				  int[] w1={0,0,0};
				  w1[0]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*v1[1]))*Math.cos(2*Math.PI*v1[0])/3);
				  w1[1]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*v1[1]))*Math.sin(2*Math.PI*v1[0])/3);
				  if (v2[1]<=.5 && v1[1]<=.5) {
			        int[] w2={0,0,0};
				    w2[0]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*v2[1]))*Math.cos(2*Math.PI*v2[0])/3);
				    w2[1]=MathEqns.round(SZ+SZ*(2+Math.cos(2*Math.PI*v2[1]))*Math.sin(2*Math.PI*v2[0])/3);
				    g.drawLine(w1[0],w1[1],w2[0],w2[1]);
			        if (i==7 && getLabelShown())
			          g.drawString(""+(char)('A'+ID%26)+""+(ID/26),w1[0],w1[1]);
				  }
				  else g.drawOval(w1[0],w1[1],1,1);
			      break;
			  }
			}
	}
	public void update() {
		super.update();
	}
}

class ToroidalBISECTOR extends ToroidalLine {

	public ToroidalBISECTOR(int t, LinkedList<GeoConstruct> clickedList) {super(t, clickedList);}
	public void draw(Graphics g, int SZ, boolean New) {
		super.draw(g, SZ, New);
	}
	public void update() {
		if (getValidNew()) {
			int horiz=0,vert=0;
			double[] aa={0,0,0},bb={0,0,0},cc={0,0,0};
			constList.get(0).getNewXYZ(aa);
			constList.get(1).getNewXYZ(bb);
			constList.get(2).getNewXYZ(cc);
			double dist=2;
			horiz=0;vert=0;
			for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
			if (Math.sqrt((aa[0]-bb[0]+i)*(aa[0]-bb[0]+i)+(aa[1]-bb[1]+j)*(aa[1]-bb[1]+j))<dist) {
			  dist=Math.sqrt((aa[0]-bb[0]+i)*(aa[0]-bb[0]+i)+(aa[1]-bb[1]+j)*(aa[1]-bb[1]+j));
			  horiz=i; vert=j;
		    }
			aa[0]+=horiz; aa[1]+=vert;
			dist=2; horiz=0;vert=0;
			for (int i=-1;i<2;i++) for (int j=-1;j<2;j++)
			if (Math.sqrt((cc[0]-bb[0]+i)*(cc[0]-bb[0]+i)+(cc[1]-bb[1]+j)*(cc[1]-bb[1]+j))<dist) {
			  dist=Math.sqrt((cc[0]-bb[0]+i)*(cc[0]-bb[0]+i)+(cc[1]-bb[1]+j)*(cc[1]-bb[1]+j));
			  horiz=i; vert=j;
			}
			cc[0]+=horiz; cc[1]+=vert;
			EuclideanPoint a=new EuclideanPoint(POINT,aa);
			EuclideanPoint b=new EuclideanPoint(POINT,bb);
			EuclideanPoint c=new EuclideanPoint(POINT,cc);
			LinkedList<GeoConstruct>tempList=new LinkedList<GeoConstruct>();
			tempList.add(b);	tempList.add(c);
			EuclideanLine d=new EuclideanLine(LINE,tempList);
			d.update();	d.getNewXYZ(v1);
			EuclideanCircle e=new EuclideanCircle(CIRCLE,b,a);
			EuclideanPoint f1,f2;
			f1=((EuclideanLine)d).intersect(0,(EuclideanCircle)e);
			f1.update();	f1.getNewXYZ(v1);	f1.setXYZ(v1);
			f2=((EuclideanLine)d).intersect(1,(EuclideanCircle)e);
			f2.update();	f2.getNewXYZ(v2);	f2.setXYZ(v2);
			tempList.clear();	tempList.add(a);	tempList.add(f1);
			EuclideanPoint g1=new EuclideanMIDPT(MIDPT,tempList,v1);
			g1.update();	g1.getNewXYZ(v1);	g1.setXYZ(v1);
			tempList.clear();	tempList.add(a);	tempList.add(f2);
			EuclideanPoint g2=new EuclideanMIDPT(MIDPT,tempList,v2);
			g2.update();	g2.getNewXYZ(v2);	g2.setXYZ(v2);
			tempList.clear();	tempList.add(a);	tempList.add(c);
			EuclideanPoint g3=new EuclideanMIDPT(MIDPT,tempList,bb);
			g3.update();	g3.getNewXYZ(bb);	g3.setXYZ(bb);
			if (MathEqns.norm(v1,bb)>MathEqns.norm(v2,bb)) {
				g1=g2;
			}
			tempList.clear();	tempList.add(b);	tempList.add(g1);
			EuclideanLine h=new EuclideanLine(LINE,tempList);
			h.update();	h.getNewXYZ(v1);
			if (MathEqns.norm(v1)<=.0001) {
				tempList.clear();	tempList.add(a);	tempList.add(c);
				d=new EuclideanLine(LINE,tempList);
				d.update();			d.getNewXYZ(v1);	d.setXYZ(v1);
				tempList.clear();	tempList.add(d);	tempList.add(b);
				h=new EuclideanPERP(PERP,tempList);
				h.update();	h.getNewXYZ(v1);
			}
			if (MathEqns.norm(v1)>.0001) setNewXYZ(v1);
			else setValidNew(false);
		}
	}
}
